#include "defines/axes.h"
#include "octree.h"
#include "combinedObject.h"
#include <iostream>


Octree::Octree(const Vector3D & _min, const Vector3D &_max, const Color &color): min(_min), max(_max), color(color){
    staticOctree = true;
    object = NULL;

    root = NULL;
}

Octree::Octree(Object *object, int _depth) : object(object){
    staticOctree = false;

    calculateMinMax();

    makeTree(_depth);
}

Octree::~Octree(){
    delete root;
}

void Octree::draw(bool _wireframe, bool _border){
    if(staticOctree) root->draw(_wireframe, _border, color, min, max);
    else{
//        switch(object->getType()){
//            case CUBOID:{
//                switch(object->getRotationX()){
//                    case R0:{
//
//                        break;
//                    }
//                }
//                break;
//            }
//        }


        root->draw(_wireframe, _border, object->getColor(), min, max);
    }
}

Vector3D Octree::getMin() const{
    return min;
}

Vector3D Octree::getMax() const{
    return max;
}

double Octree::volume() const{
    double a = (min - Vector3D(max.x, min.y, min.z)).length();
    return root->volume() * a * a * a;
}

double Octree::area(){
    double a = (min - Vector3D(max.x, min.y, min.z)).length();
    double b = root->area();
//    std::cout <<"porcentagem da area:" <<b <<std::endl;
    return b * a * a;
}

void Octree::rotate(int _rX, int _rY, int _rZ){
    root->rotate(_rX, _rY, _rZ);
}

void Octree::setRoot(OctreeNode *_r){
    root = _r;
}

void Octree::calculateMinMax(){
    double minX, maxX;
    double minY, maxY;
    double minZ, maxZ;
    object->getMinMax(&minX, &maxX, X_AXIS);
    object->getMinMax(&minY, &maxY, Y_AXIS);
    object->getMinMax(&minZ, &maxZ, Z_AXIS);

    double maxMinX = maxX - minX;
    double maxMinY = maxY - minY;
    double maxMinZ = maxZ - minZ;
    double larger = maxMinX;
    if(maxMinY > larger) larger = maxMinY;
    if(maxMinZ > larger) larger = maxMinZ;

    min = Vector3D(minX - ((larger - maxMinX)/2.0), minY - ((larger - maxMinY)/2.0), minZ - ((larger - maxMinZ)/2.0));
    max = Vector3D(maxX + ((larger - maxMinX)/2.0), maxY + ((larger - maxMinY)/2.0), maxZ + ((larger - maxMinZ)/2.0));
}

void Octree::makeTree(int _depth){
    root = makeTreeRecursive(_depth, object);
}


OctreeNode *Octree::makeTreeRecursive(int _depth, Object *_obj){
    if(_obj->isPrimitive()){
        OctreeNode *result;
        result = new OctreeNode();
        result->create(_obj, _depth, min, max);

        result->rotate(_obj->getRotationX(), _obj->getRotationY(), _obj->getRotationZ());
        return result;
    }

//    std::vector<OctreeNode *> roots;
    OctreeNode *root1;
    OctreeNode *root2;
    OctreeNode *combination;

    //primeiro nó na lista
    root1 = makeTreeRecursive(_depth, ((CombinedObject *)_obj)->getObject(0));

    //segundo nó na lista
    root2 = makeTreeRecursive(_depth, ((CombinedObject *)_obj)->getObject(1));

    //cria a proxima combinação e o proximo nó
    unsigned int i;
    unsigned int size = ((CombinedObject *)_obj)->getObjectsSize();
    for(i = 2; i < size; i++){
        //combinando os dois ultimos
        combination = combine(root1, root2, _obj->getType());
        delete root1;
        delete root2;
        root1 = combination;

        //criando o proximo
        root2 = makeTreeRecursive(_depth, ((CombinedObject *)_obj)->getObject(i));
    }
    //fica faltando a ultima combinação, que é de fato o root
    combination = combine(root1, root2, _obj->getType());
    delete root1;
    delete root2;

    combination->rotate(_obj->getRotationX(), _obj->getRotationY(), _obj->getRotationZ());
    return combination;
}
